题目链接
PTA上的一道 Dijkstra 题目,对该算法不太熟悉,做了挺久,最后测试点5,7过不去,看大佬的题解,发现错误的不仅仅是两个测试点,整个思路都有问题。
错误分析
错误代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 #include <bits/stdc++.h> using namespace std ;int g[510 ][510 ], dist[510 ], w[510 ], pre[510 ], q[510 ];bool st[510 ];int main () { int bike, n, index, m; scanf ("%d%d%d%d" , &bike, &n, &index, &m); memset (g, 0x3f , sizeof g); memset (dist, 0x3f , sizeof dist); memset (pre, -1 , sizeof pre); dist[0 ] = 0 ; int x = bike / 2 ; for (int i = 1 ; i <= n; i++) { scanf ("%d" , &w[i]); w[i] -= x; } for (int i = 0 ; i < m; i++) { int a, b, c; scanf ("%d%d%d" , &a, &b, &c); if (g[a][b] > c) { g[a][b] = g[b][a] = c; if (a == 0 ) pre[b] = 0 ; } } for (int j = 0 ; j < n; j++) { int t = -1 ; for (int i = 0 ; i <= n; i++) if (!st[i] && (t == -1 || dist[t] > dist[i])) t = i; st[t] = true ; for (int i = 0 ; i <= n; i++) { if (!st[i] && g[t][i] != 0x3f3f3f3f ) { if (dist[i] > dist[t] + g[t][i]) { dist[i] = dist[t] + g[t][i]; q[i] = q[t] + w[i]; pre[i] = t; } else if (dist[i] == dist[t] + g[t][i]) { if (abs (q[t] + w[i]) < abs (w[i])) q[i] = q[t] + w[i], pre[i] = t; } } } } if (q[index] < 0 ) printf ("%d " , -q[index]); else printf ("0 " ); vector <int > v; for (int i = index; pre[i] != -1 ; i = pre[i]) { v.push_back(i); } printf ("0" ); for (int i = v.size() - 1 ; i >= 0 ; i--) printf ("->%d" , v[i]); if (q[index] > 0 ) printf (" %d" , q[index]); else printf (" 0" ); return 0 ; }
错误点:
与1003 Emergency 不同,本题不满足最优子结构,不能只用 Dijkstra 来做,挺严重的一错误。
只能沿着最短路径的方向收集多余自行车,分给后面的节点;后面节点多出来的不能填到前面去,只能计入回收总量。
例如路径上自行车数为5->0->10,并不能把最后一个节点上挪5个给中间的,需要送出5个,并回收5个。
所以总需求量不能用 bike / 2 * 节点数 - 现有数来计算。
否则测试点5、7均无法通过。
大神解法
柳神代码:1018. Public Bike Management (30)-PAT甲级真题(Dijkstra + DFS)
传统 Dijkstra 算法使用额外一维数组pre[]
记录前导节点,通过pre[]
可以倒推找回路径。若存在多条路径,则把pre[]
扩展成二维数组pre[][]
即可,pre[i][j]
表示节点i的第j个前导节点。
从目标节点出发,可以直接找出其下一个节点。这样便相当于有了一个从目标节点出发,回到源节点的单向图。
深度遍历目标节点,即可考察完所有最短路径。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 #include <iostream> #include <algorithm> #include <vector> using namespace std ;const int inf = 99999999 ;int cmax, n, sp, m;int minNeed = inf, minBack = inf;int e[510 ][510 ], dis[510 ], weight[510 ]; bool visit[510 ]; vector <int > pre[510 ], path, temppath; void dfs (int v) { temppath.push_back(v); if (v == 0 ) { int need = 0 , back = 0 ; for (int i = temppath.size() - 1 ; i >= 0 ; i--) { int id = temppath[i]; if (weight[id] > 0 ) { back += weight[id]; } else { if (back > (0 - weight[id])) { back += weight[id]; } else { need += ((0 - weight[id]) - back); back = 0 ; } } } if (need < minNeed) { minNeed = need; minBack = back; path = temppath; } else if (need == minNeed && back < minBack) { minBack = back; path = temppath; } temppath.pop_back(); return ; } for (int i = 0 ; i < pre[v].size(); i++) dfs(pre[v][i]); temppath.pop_back(); } int main () { fill(e[0 ], e[0 ] + 510 * 510 , inf); fill(dis, dis + 510 , inf); scanf ("%d%d%d%d" , &cmax, &n, &sp, &m); for (int i = 1 ; i <= n; i++) { scanf ("%d" , &weight[i]); weight[i] = weight[i] - cmax / 2 ; } for (int i = 0 ; i < m; i++) { int a, b; scanf ("%d%d" , &a, &b); scanf ("%d" , &e[a][b]); e[b][a] = e[a][b]; } dis[0 ] = 0 ; for (int i = 0 ; i <= n; i++) { int u = -1 , minn = inf; for (int j = 0 ; j <= n; j++) { if (visit[j] == false && dis[j] < minn) { u = j; minn = dis[j]; } } if (u == -1 ) break ; visit[u] = true ; for (int v = 0 ; v <= n; v++) { if (visit[v] == false && e[u][v] != inf) { if (dis[v] > dis[u] + e[u][v]) { dis[v] = dis[u] + e[u][v]; pre[v].clear(); pre[v].push_back(u); }else if (dis[v] == dis[u] + e[u][v]) { pre[v].push_back(u); } } } } dfs(sp); printf ("%d 0" , minNeed); for (int i = path.size() - 2 ; i >= 0 ; i--) printf ("->%d" , path[i]); printf (" %d" , minBack); return 0 ; }